﻿////////////////////////////////////////////////////////////////////////////////////////////
// File: XNAParticle.cs                                                                   //
// Author: Mike Binnix                                                                    //
// Desc: Base particle class that contains the information for it to work in the physics  //
//       simulation.  More complex particles can be created from this class.              //
////////////////////////////////////////////////////////////////////////////////////////////

using System;
using Microsoft.Xna.Framework;

namespace SlothEngine.Physics
{
    public class XNAParticle
    {
        #region Attributes
        protected float inverseMass;
        protected float damping;
        protected Vector2 position;
        protected Vector2 velocity;
        protected Vector2 forceAccum;
        protected Vector2 acceleration;
        protected bool destroy;
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets objects mass.  Setting to 0 will set the object to the smallest allowable mass.
        /// </summary>
        public float Mass
        {
            get
            {
                if (inverseMass == 0.0f)
                    return float.MaxValue;
                else
                    return 1 / inverseMass;
            }
            set
            {
                if (value == 0.0f)
                    inverseMass = 1.0f / float.Epsilon;
                else
                    inverseMass = Math.Abs(1.0f / value);
            }
        }
        /// <summary>
        /// Gets or sets objects inverse mass.  An inverse mass of 0 means the object can not be moved.
        /// </summary>
        public float InverseMass
        {
            get
            {
                return inverseMass;
            }
            set
            {
                inverseMass = Math.Abs(value);
            }
        }
        /// <summary>
        /// Gets if object can be moved or not.
        /// </summary>
        public bool CanMove
        {
            get
            {
                return inverseMass > 0.0f;
            }
        }
        /// <summary>
        /// Gets or sets the drag imposed on the object each update.
        /// </summary>
        public float Damping
        {
            get
            {
                return damping;
            }
            set
            {
                damping = value;
            }
        }
        /// <summary>
        /// Gets of sets the position of the object as a Vector2.
        /// </summary>
        public Vector2 Position
        {
            get{
                return position;
            }
            set{
                position = value;
            }
        }
        /// <summary>
        /// Gets or sets the x position of an object.
        /// </summary>
        public float PositionX
        {
            get
            {
                return position.X;
            }
            set
            {
                position.X = value;
            }
        }
        /// <summary>
        /// Gets of sets the y position of an object.
        /// </summary>
        public float PositionY
        {
            get
            {
                return position.Y;
            }
            set
            {
                position.Y = value;
            }
        }
        /// <summary>
        /// Gets or sets the velocity of an object as a Vector2.
        /// </summary>
        public Vector2 Velocity
        {
            get
            {
                return velocity;
            }
            set
            {
                velocity = value;
            }
        }
        /// <summary>
        /// Gets or sets the x velocity of an object.
        /// </summary>
        public float VelocityX
        {
            get
            {
                return velocity.X;
            }
            set
            {
                velocity.X = value;
            }
        }
        /// <summary>
        /// Gets or sets the y velocity of an object.
        /// </summary>
        public float VelocityY
        {
            get
            {
                return velocity.Y;
            }
            set
            {
                velocity.Y = value;
            }
        }
        /// <summary>
        /// Gets the accumulated force on an object.
        /// </summary>
        public Vector2 ForceAccum
        {
            get
            {
                return forceAccum;
            }
        }
        /// <summary>
        /// Gets the accumulated x force on an object.
        /// </summary>
        public float ForceAccumX
        {
            get
            {
                return forceAccum.X;
            }
        }
        /// <summary>
        /// Gets the accumulated y force on an object.
        /// </summary>
        public float ForceAccumY
        {
            get
            {
                return forceAccum.Y;
            }
        }
        /// <summary>
        /// Gets or sets the acceleration on an object.
        /// </summary>
        public Vector2 Acceleration
        {
            get
            {
                return acceleration;
            }
            set
            {
                acceleration = value;
            }
        }
        /// <summary>
        /// Gets or sets the x acceleration on the object.
        /// </summary>
        public float AccelerationX
        {
            get
            {
                return acceleration.X;
            }
            set
            {
                acceleration.X = value;
            }
        }
        /// <summary>
        /// Gets or sets the y acceleration on the object.
        /// </summary>
        public float AccelerationY
        {
            get
            {
                return acceleration.Y;
            }
            set
            {
                acceleration.Y = value;
            }
        }
        /// <summary>
        /// Gets or sets whether the particle should be destroyed.
        /// </summary>
        public bool Destroy
        {
            get
            {
                return destroy;
            }
            set
            {
                destroy = value;
            }
        }
        #endregion

        #region Constructors
        public XNAParticle()
        {
            inverseMass = 0.0f;
            damping = 0.0f;
            position = Vector2.Zero;
            velocity = Vector2.Zero;
            forceAccum = Vector2.Zero;
            acceleration = Vector2.Zero;
            destroy = false;
        }

        public XNAParticle(float mass, float damping, Vector2 position, Vector2 velocity, Vector2 acceleration)
        {
            this.Mass = mass;
            this.damping = damping;
            this.position = position;
            this.velocity = velocity;
            this.acceleration = acceleration;
            destroy = false;
        }
        #endregion

        #region Methods
        /// <summary>
        /// Updates particle object.
        /// </summary>
        /// <param name="gameTime">Current Game time.</param>
        public virtual void Update(GameTime gameTime)
        {
            // Objects with 0 mass cant be moved.
            if (inverseMass == 0.0f)
                return;

            // Update position
            position += velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;

            // Update acceleration
            Vector2 resultAccel = acceleration;
            resultAccel += forceAccum * inverseMass;

            // Update velocity
            velocity += resultAccel * (float)gameTime.ElapsedGameTime.TotalSeconds;
            velocity = Vector2.Multiply(velocity, (float)Math.Pow(damping, gameTime.ElapsedGameTime.TotalSeconds));

            ClearForceAccum();
        }

        /// <summary>
        /// Clears the accumulated forces.
        /// </summary>
        public void ClearForceAccum()
        {
            forceAccum = Vector2.Zero;
        }

        /// <summary>
        /// Adds a force the the particle.
        /// </summary>
        /// <param name="force">Force to add.</param>
        public void AddForce(Vector2 force)
        {
            forceAccum += force;
        }
        #endregion
    }
}
